#ifndef IOCP_PLEAFLES_H
#define IOCP_PLEAFLES_H

#include <QObject>
#include <QLibrary>
#include <QDataStream>
#include <QDebug>

#include <windows.h>

/*
 * 鹰:Send函数是用主线程或其他线程发送(非IOCP线程),dll会将数据复制再用IOCP线程进行发送
 * 其他回调函数,都是IOCP线程,所以尽快处理数据,或将数据拷贝后POST给处理线程!
 * !!!注意Close函数回调后会释放Socket资源,所以涉及到Socket资源的不能POST给主线程!!!
*/

#define FUNCTION_CALL_TYPE __cdecl//__stdcall,  约定dll调用格式(跨语言)

//导出函数说明

// 0.新建一个IOCP对象
typedef void* (FUNCTION_CALL_TYPE *_CreateIOCP)();

// A.加载Socket库
typedef bool (FUNCTION_CALL_TYPE *_LoadSocketLib)(void *pIOCP);

// a.启动服务器
typedef bool (FUNCTION_CALL_TYPE *_Start)(void *pIOCP);

// b.停止服务器
typedef void (FUNCTION_CALL_TYPE *_Stop)(void *pIOCP);

// B.卸载Socket库，彻底完事
typedef void (FUNCTION_CALL_TYPE *_UnloadSocketLib)(void *pIOCP);

// 断开某个用户
typedef void (FUNCTION_CALL_TYPE *_Close)(void *pIOCP,int socketID, void *pIOCPSocketContext);
// 手动释放SocketContext资源
typedef int (FUNCTION_CALL_TYPE *_ReleaseSocketContext)(void *pIOCP, void *pIOCPSocketContext);

// 获取某个Socket的IP地址和端口
typedef void (FUNCTION_CALL_TYPE *_SocketIPPort)(void *pIOCP, u_long *ip, u_short *port, void *pIOCPSocketContext);

// 给客户端发送数据
typedef bool (FUNCTION_CALL_TYPE *_Send)(void *pIOCP, char *buf, unsigned long len, int socketID, void *pIOCPSocketContext);

//遍历所有Sockets
typedef void (FUNCTION_CALL_TYPE *_TraverseSockets)(void *pIOCP);

typedef void (FUNCTION_CALL_TYPE *_Debug)(void *pIOCP, int value);

// 3.释放资源(Dll的delete,用来释放回调函数的参数(Msg))
typedef void (FUNCTION_CALL_TYPE *_ReleaseIOCP)(void *p);

// 4.其他辅助函数
// 获得本机的处理器数量
typedef int (FUNCTION_CALL_TYPE *_GetNoOfProcessors)();
// 获得本机的IP地址
typedef char* (FUNCTION_CALL_TYPE *_GetLocalIP)();

// 设置/获取 Socket额外数据
typedef void (FUNCTION_CALL_TYPE *_SetSocketExtraData)(void *pIOCP, void *pData,void *pIOCPSocketContext);
typedef void*(FUNCTION_CALL_TYPE *_SocketExtraData)(void *pIOCP, void *pIOCPSocketContext);

// 设置两个创建和释放SocketExtraData的回调函数(用来配合IOCP的池!)
typedef void (FUNCTION_CALL_TYPE *_SetIOCPSetSocketExtraDataCallback)(void* fn);
typedef void (FUNCTION_CALL_TYPE *_SetIOCPUnsetSocketExtraDataCallback)(void *fn);
typedef void (FUNCTION_CALL_TYPE *_SetIOCPInitSocketExtraDataCallback)(void* fn);
typedef void (FUNCTION_CALL_TYPE *_SetIOCPReleaseSocketExtraDataCallback)(void *fn);


// 3.初始化
// 设置监听IP和端口
typedef void (FUNCTION_CALL_TYPE *_SetIP)(void *pIOCP, const char* strIP);
typedef void (FUNCTION_CALL_TYPE *_SetPort)(void *pIOCP, const int& nPort);

// 设置flag
typedef void (FUNCTION_CALL_TYPE *_SetIOCPFlag)(void *pIOCP, unsigned long flags);

//或者设置回调函数:
typedef void (FUNCTION_CALL_TYPE *_SetIOCPMessageCallback)(void *pIOCP, void* fn, void *param);
typedef void (FUNCTION_CALL_TYPE *_SetIOCPDbgMessageCallback)(void *pIOCP, void* fn, void *param);

typedef void (FUNCTION_CALL_TYPE *_SetIOCPAcceptCallback)(void *pIOCP, void* fn, void *param);
typedef void (FUNCTION_CALL_TYPE *_SetIOCPRecvCallback)(void *pIOCP, void* fn, void *param);
typedef void (FUNCTION_CALL_TYPE *_SetIOCPSendCallback)(void *pIOCP, void* fn, void *param);
typedef void (FUNCTION_CALL_TYPE *_SetIOCPCloseCallback)(void *pIOCP, void* fn, void *param);

typedef void (FUNCTION_CALL_TYPE *_SetIOCPWatchCallback)(void *pIOCP, void* fn, void *param);

typedef void (FUNCTION_CALL_TYPE *_SetIOCPTraverseCallback)(void *pIOCP, void* fn, void *param);

// 设置 最大连接数,-1表示不限; listen第二个参数,默认为SOMAXCONN; 一次最多投多少个Accept,默认MAX_POST_ACCEPT
typedef void (FUNCTION_CALL_TYPE *_SetIOCPCountProperty)(void *pIOCP, int maxSocketCount, int listenBacklog, int maxAcceptIoContextCount, int socketContextReserveCount);

// 设置两个时间
typedef void (FUNCTION_CALL_TYPE *_SetWatchThreadInterval)(void *pIOCP, unsigned long ms);	//watch线程扫描间隔(毫秒)
typedef void (FUNCTION_CALL_TYPE *_SetSocketTimeoutSecond)(void *pIOCP, int s);	//socket超时(秒)

// 设置心跳包数据
typedef bool (FUNCTION_CALL_TYPE *_SetHeartData)(void *pIOCP, const char d[], int l);	//socket超时(秒)

typedef void (FUNCTION_CALL_TYPE *_SetSocketSendAndRecvBufferLength)(void *pIOCP, int sendBufferLength, int recvBufferLength);





//服务端 的 客户信息,一次封装
class _SocketInfo
{
public:
    explicit _SocketInfo():
        m_dsIn(&m_baIn,QIODevice::ReadWrite),
        m_ulDataSize(0),

        m_dsOut(&m_baOut,QIODevice::ReadWrite),
        m_bOutInit(false)
        //packageID(0),
    {
        //qDebug()<<"devices:"<<m_dsIn.device();
    }
    virtual ~_SocketInfo(){}

    virtual void _pool_init(){}

    virtual void _pool_release()
    {
        m_SocketContext.nSocketID = -1;
        m_SocketContext.pIOCPSocketContext = nullptr;
        m_SocketContext.pSocketInfo = nullptr;

        m_bOutInit = false;
        //packageID = 0;
        m_ulDataSize = 0;

        m_dsOut.device()->reset();  //!!!必须加这句,否则前面的无法clear
        m_baOut.clear();

        m_dsIn.device()->reset();
        m_baIn.clear();
    }


public:

    //Socket上下文(用来 标识一个唯一客户对象 )
    struct _SocketContext
    {
        _SocketContext(): nSocketID(-1), pIOCPSocketContext(nullptr), pSocketInfo(nullptr)
        {}

        int         nSocketID;              //IOCP传来的SocketID,自增唯一(用来验证,并非真实Socket句柄)
        void        *pIOCPSocketContext;    //IOCP传来的SocketContext句柄
        _SocketInfo *pSocketInfo;           //SocketInfo指针
    }m_SocketContext;


    //读入(接受用)
    QByteArray      m_baIn;         //包含缓存区
    QDataStream     m_dsIn;         //包含已读指针,数据操作

    unsigned long   m_ulDataSize;   //保存包解析后的 数据长度,也就是去除头部的数据长度(包真实接受长度 不等于 数据长度)!!!


    //发出(发送用)
    QByteArray      m_baOut;        //包含缓存区
    QDataStream     m_dsOut;        //包含已读指针,数据操作
    bool            m_bOutInit;     //是否为 发送前已初始化过
    //int packageID;

};




//IOCP模型,一次封装
class IOCP_Pleafles : public QObject
{
    Q_OBJECT
public:
    explicit IOCP_Pleafles(QObject *parent = nullptr);
    virtual ~IOCP_Pleafles();

signals:
    //void s_Message(QString msg);
    //void s_Debug(QString msg);

    //void s_accept(void *pIOCPSocketContext);
    //void s_disconnect(int type, void *pIOCPSocketContext,void*param);
    //void s_readyRead(QByteArray baData, quint8 controlByte, void *pIOCPSocketContext);


public slots:
    virtual bool InitOnce();

public:

//dll函数:面向过程
    _CreateIOCP CreateIOCP;
    _LoadSocketLib LoadSocketLib;


    //带__是需要再封装的(把IOCP指针封装)
    _SetIP __SetIP;
    _SetPort __SetPort;

    _SetIOCPFlag __SetIOCPFlag;

    _Start __Start;
    _Stop __Stop;
    _Send __Send;

    _Close __Close;
    _ReleaseSocketContext __ReleaseSocketContext;

    _TraverseSockets __TraverseSockets;

    _Debug __Debug;

    _ReleaseIOCP ReleaseIOCP;

    _GetNoOfProcessors GetNoOfProcessors;
    _GetLocalIP GetLocalIP;

    _UnloadSocketLib UnloadSocketLib;


    _SetIOCPMessageCallback __SetIOCPMessageCallback;
    _SetIOCPDbgMessageCallback __SetIOCPDbgMessageCallback;
    _SetIOCPAcceptCallback __SetIOCPAcceptCallback;
    _SetIOCPRecvCallback __SetIOCPRecvCallback;
    _SetIOCPSendCallback __SetIOCPSendCallback;
    _SetIOCPCloseCallback __SetIOCPCloseCallback;
    _SetIOCPWatchCallback __SetIOCPWatchCallback;
    _SetIOCPTraverseCallback __SetIOCPTraverseCallback;

    _SetIOCPSetSocketExtraDataCallback SetIOCPSetSocketExtraDataCallback;
    _SetIOCPUnsetSocketExtraDataCallback SetIOCPUnsetSocketExtraDataCallback;
    _SetIOCPInitSocketExtraDataCallback SetIOCPInitSocketExtraDataCallback;
    _SetIOCPReleaseSocketExtraDataCallback SetIOCPReleaseSocketExtraDataCallback;


    _SetSocketExtraData __SetSocketExtraData;
    _SocketExtraData __SocketExtraData;

    _SocketIPPort __SocketIPPort;

    _SetIOCPCountProperty __SetIOCPCountProperty;

    _SetWatchThreadInterval __SetWatchThreadInterval;
    _SetSocketTimeoutSecond __SetSocketTimeoutSecond;

    _SetHeartData __SetHeartData;

    _SetSocketSendAndRecvBufferLength __SetSocketSendAndRecvBufferLength;


public:
//二次封装Dll,形成类对象的形式

    // 2.设置监听IP和端口
    inline void SetServerIP( const char* strIP){__SetIP(m_hIOCP,strIP);}
    inline void SetServerPort(const int& nPort){__SetPort(m_hIOCP,nPort);}

    // 设置flags
    inline void SetIOCPFlag(unsigned long flags){__SetIOCPFlag(m_hIOCP,flags);}

    // 3.启动服务器
    inline bool Start(){return __Start(m_hIOCP);}

    //4.给客户端发送数据
    inline bool Send(char *buf,unsigned long len,int socketID,void *pIOCPSocketContext){return __Send(m_hIOCP,buf,len,socketID,pIOCPSocketContext);}

    // a.停止服务器
    inline void Stop(){__Stop(m_hIOCP);}

    inline void Close(int socketID, void *pIOCPSocketContext){__Close(m_hIOCP,socketID,pIOCPSocketContext);}
    inline int ReleaseSocketContext(void *pIOCPSocketContext){return __ReleaseSocketContext(m_hIOCP,pIOCPSocketContext);}

    // 遍历Sockets(会调用回调函数)
    inline void TraverseSockets(){__TraverseSockets(m_hIOCP);}

    // 调试功能(从IOCP返回一些信息)
    inline void Debug(int value){__Debug(m_hIOCP,value);}


    // 1.或者设置回调函数:
    inline void SetIOCPMessageCallback(void* fn, void *param){__SetIOCPMessageCallback(m_hIOCP,fn,param);}
    inline void SetIOCPDbgMessageCallback(void* fn, void *param){__SetIOCPDbgMessageCallback(m_hIOCP,fn,param);}
    inline void SetIOCPAcceptCallback(void* fn, void *param){__SetIOCPAcceptCallback(m_hIOCP,fn,param);}
    inline void SetIOCPRecvCallback(void* fn, void *param){__SetIOCPRecvCallback(m_hIOCP,fn,param);}
    inline void SetIOCPSendCallback(void* fn, void *param){__SetIOCPSendCallback(m_hIOCP,fn,param);}
    inline void SetIOCPCloseCallback(void* fn, void *param){__SetIOCPCloseCallback(m_hIOCP,fn,param);}
    inline void SetIOCPWatchCallback(void* fn, void *param){__SetIOCPWatchCallback(m_hIOCP,fn,param);}
    inline void SetIOCPTraverseCallback(void* fn, void *param){__SetIOCPTraverseCallback(m_hIOCP,fn,param);}


    // 2.其他设置
    inline void SetSocketExtraData(void *pData,void *pIOCPSocketContext){__SetSocketExtraData(m_hIOCP,pData,pIOCPSocketContext);}
    inline void *SocketExtraData(void *pIOCPSocketContext){return __SocketExtraData(m_hIOCP,pIOCPSocketContext);}

    inline void SocketIPPort(u_long *ip, u_short *port, void *pIOCPSocketContext){__SocketIPPort(m_hIOCP,ip,port,pIOCPSocketContext);}
    inline void SetIOCPCountProperty(int maxSocketCount, int listenBacklog, int maxAcceptIoContextCount, int socketContextReserveCount){__SetIOCPCountProperty(m_hIOCP, maxSocketCount, listenBacklog, maxAcceptIoContextCount, socketContextReserveCount);}

    inline void SetWatchThreadInterval(unsigned long ms){__SetWatchThreadInterval(m_hIOCP,ms);}
    inline void SetSocketTimeoutSecond(int s){__SetSocketTimeoutSecond(m_hIOCP,s);}

    inline void SetHeartData(const char d[], int l){__SetHeartData(m_hIOCP,d,l);}

    inline void SetSocketSendAndRecvBufferLength(int sendBufferLength, int recvBufferLength){__SetSocketSendAndRecvBufferLength(m_hIOCP, sendBufferLength, recvBufferLength);}


    /*void *getIOCP()
    {
        return m_hIOCP;
    }*/



//三次封装(使用QDataStream和QByteArray作为缓冲区和控制,加入了包长度):
public:

//A.使用每个_SocketInfo自带的数据区发送(要注意多线程时 非线程安全!!!)
    //1.初始化缓冲区
    QDataStream &sendData(_SocketInfo *pSocketInfo, quint8 outControlbyte = 0)
    {
        //如果 之前不存在发送数据,则初始化一下(清空缓存 和 加入大小)
        if(pSocketInfo->m_bOutInit == false)
        {
            //qDebug()<<"------发送前初始化前:"<<block.size();
            pSocketInfo->m_dsOut.device()->seek(0);  //!!必须加这句,否则前面的无法clear
            pSocketInfo->m_baOut.clear();

            if(m_baPackageHeaderFlag.length() > 0)  //复制包头标志
                pSocketInfo->m_dsOut.writeRawData(m_baPackageHeaderFlag,m_baPackageHeaderFlag.length());

            pSocketInfo->m_dsOut << quint8(outControlbyte);    //控制字符

            pSocketInfo->m_dsOut << quint32(0); //预留 包长度
            pSocketInfo->m_bOutInit = true;

            //qDebug()<<"------发送前初始化:"<<block.size();
        }
        return pSocketInfo->m_dsOut;
    }/*
    bool send(_SocketInfo *pSocketInfo)
    {
        //if(block.size() < 32768 / 2)
        if(pSocketInfo->m_bOutInit)
        {
            //unsigned long totalDataLength = pSocketInfo->m_baOut.size() - sizeof(quint32);
            //qDebug()<<"------totalDataLength(总发送):"<<totalDataLength;

            pSocketInfo->m_dsOut.device()->seek(0);
            pSocketInfo->m_dsOut << (quint32)(pSocketInfo->m_baOut.size() - sizeof(quint32));
            pSocketInfo->m_bOutInit = false;
            return Send(pSocketInfo->pIOCPSocketContext,pSocketInfo->m_baOut.data(),pSocketInfo->m_baOut.length());

            //qDebug()<<"------已发送(write):"<<totalDataLength;
            //return ret;
        }
        return false;
    }*/

    //2.发送(自动加入包长,并 初始化flag置false)
    bool send(_SocketInfo *pSocketInfo)
    {
        //if(block.size() < 32768 / 2)
        if(pSocketInfo->m_bOutInit)
        {
            qint64 totalDataLength = pSocketInfo->m_baOut.size() - m_baPackageHeaderFlag.length() - sizeof(quint8) - sizeof(quint32);
            //qDebug()<<"------totalDataLength(总发送):"<<totalDataLength;

            pSocketInfo->m_dsOut.device()->seek(m_baPackageHeaderFlag.length() + sizeof(quint8));
            pSocketInfo->m_dsOut << (quint32)totalDataLength;
            pSocketInfo->m_bOutInit = false;


            /*packageID
            pSocketInfo->m_dsOut.device()->seek(pSocketInfo->m_baOut.size());
            pSocketInfo->m_dsOut << ++pSocketInfo->packageID;
            */

            return Send((char*)pSocketInfo->m_baOut.constData(),pSocketInfo->m_baOut.length(),pSocketInfo->m_SocketContext.nSocketID,pSocketInfo->m_SocketContext.pIOCPSocketContext);

            //qDebug()<<"------已发送(write):"<<totalDataLength;
            //return ret;
        }
        return false;
    }


public:
//B.独立缓冲区 发送
//方法2:新建一个另外的数据缓存,进行发送
    //1.将 引用 的 baOut初始化 并 返回
    //注意:和方法A不同的地方是,控制字符在这里赋值!!
    //注意:使用时注意m_baOut里初始化好的数据!将指针移到最后了!!!
    QByteArray& newSendData(QByteArray &baOut, quint8 outControlbyte = 0)
    {
        baOut.clear();
        QDataStream dsOut(&baOut,QIODevice::ReadWrite);          //包含已读指针,数据操作

        if(m_baPackageHeaderFlag.length() > 0)  //复制包头标志
            dsOut.writeRawData(m_baPackageHeaderFlag,m_baPackageHeaderFlag.length());

        dsOut << outControlbyte;    //控制字符
        dsOut << quint32(0);        //长度占位

        return baOut;
    }

    //1.返回一个已经初始化好的ByteArray.其他同上
    QByteArray newSendData(quint8 outControlbyte = 0)
    {
        QByteArray baOut;              //包含缓存区
        return newSendData(baOut, outControlbyte);
        //return newSendData(QByteArray(), outControlbyte);
    }

    //发送前先 修正baOut的长度
    QByteArray& CorrectDataLength(QByteArray& baOut)
    {
        //if(block.size() < 32768 / 2)
        //if(pSocketInfo->m_bOutInit)
        {
            QDataStream dsOut(&baOut,QIODevice::ReadWrite);          //包含已读指针,数据操作
            qint64 totalDataLength = baOut.size() - m_baPackageHeaderFlag.length() - sizeof(quint8) - sizeof(quint32);
            //qDebug()<<"------totalDataLength(总发送):"<<totalDataLength;

            dsOut.device()->seek(m_baPackageHeaderFlag.length() + sizeof(quint8));
            dsOut << (quint32)totalDataLength;

            //qDebug()<<"------已发送(write):"<<totalDataLength;
            //return ret;
        }
        //return false;
        return baOut;
    }

//发送,Socket是安全的(存在、有效、对应）,但 非线程安全(WSASend非线程安全)
    //发送baOut 1
    bool send(_SocketInfo *pSocketInfo, QByteArray &baOut)
    {
        /*
        QByteArray ba = baOut;
        QDataStream dsOut(&ba,QIODevice::ReadWrite);
        dsOut.device()->seek(ba.size());
        dsOut << ++pSocketInfo->packageID;
        return Send(pSocketInfo->pIOCPSocketContext,(char*)ba.constData(),ba.length());
        */

        return Send((char*)baOut.constData(),baOut.length(),pSocketInfo->m_SocketContext.nSocketID,pSocketInfo->m_SocketContext.pIOCPSocketContext);
    }

    //发送baOut 2
    bool send(_SocketInfo::_SocketContext socketContext, QByteArray &baOut)
    {
        return Send((char*)baOut.constData(),baOut.length(),socketContext.nSocketID,socketContext.pIOCPSocketContext);
    }



public:
//自带回调函数例子,项目中不用,需要扩展
//Fn_IOCP_Recv_Callback和Fn_IOCP_Send_Callback只是简单的接受发送,并没有加入 标识头和控制字符!!!
    static void FUNCTION_CALL_TYPE Fn_IOCP_Message_Callback(const char *msg,void *param);
    static void FUNCTION_CALL_TYPE Fn_IOCP_Accept_Callback(int socketID,void *pIOCPSocketContext,void*param);
    static void FUNCTION_CALL_TYPE Fn_IOCP_Recv_Callback(void*buf,unsigned long buflen,void *pIOCPSocketContext,void*param);
    static void FUNCTION_CALL_TYPE Fn_IOCP_Send_Callback(void*buf, unsigned long bufsize, unsigned long bufTransfered, void *pIOCPSocketContext, void*param);
    static void FUNCTION_CALL_TYPE Fn_IOCP_Close_Callback(int type, void *pIOCPSocketContext, void*param);

    static bool FUNCTION_CALL_TYPE Fn_IOCP_Watch_Callback(void *pIOCPSocketContext, void*param);
    static void FUNCTION_CALL_TYPE Fn_IOCP_Traverse_Callback(void* pIOCPSocketContext, int socketID, void *pExtraData, void*param);

    static void *FUNCTION_CALL_TYPE Fn_IOCP_SetSocketExtraData_Callback();
    static void FUNCTION_CALL_TYPE Fn_IOCP_UnsetSocketExtraData_Callback(void *pExtraData);


//数据成员
public:
    //数据包的头标志(用来验包)
    QByteArray  m_baPackageHeaderFlag;

private:
    void        *m_hIOCP;       //pIOCP句柄
    QLibrary    m_libIOCP;      //IOCP的Dll库

    bool        m_bInitOnce;    //IOCP初始化Flag

};

#endif // IOCP_PLEAFLES_H
